﻿// FileUtils.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//
#include <Windows.h>
#include <iostream>
#include <filesystem>
#include <sstream>
#include "BackupFS.h"

#include "db/BackupFSDB.h"
#include "../cmdlinefile/cmdtest.h"
#include "BackupFSUtils.h"
#include "../logger/BackupFSLogger.h"
#include <chrono>
#include <thread> 
#include <future>

namespace fs = std::filesystem;
using namespace std;
using namespace BackupFS;
#include <iostream>
#include <windows.h>
std::shared_ptr<BackupVFS> _BackupFS;
std::string toHex(int uc )
{
	char hexStr[32];
	sprintf_s(hexStr, 32, "%02X", static_cast<unsigned char>(uc));
	return std::string(hexStr);
}
std::string LongLongtoHex(LONGLONG uc)
{
	char hexStr[32];
	sprintf_s(hexStr, 32, "%08I64X", uc);
	return std::string(hexStr);
}
void SetColor(int textColor, int bgColor) 
{
	HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
	SetConsoleTextAttribute(hConsole, (bgColor << 4) | textColor);
}

bool HexDump(const std::wstring& file, LONGLONG offset, LONGLONG length)
{
	if (!fs::exists(file))
	{
		wcout << "The file " << file << " doesn't exist" << endl;
		return false;
	}
	HANDLE h = CreateFile(file.c_str(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
	if (h == INVALID_HANDLE_VALUE)
	{
		wcout << L"Can't open the file " << file.c_str() << L", error is " << GetLastError() << std::endl;
		return false;
	}
	LARGE_INTEGER file_offset = { 0 };
	file_offset.QuadPart = offset;
	if (!SetFilePointerEx(h, file_offset, NULL, FILE_BEGIN))
	{
		wcout << L"Can't seek the file " << file.c_str() << L", error is " << GetLastError() << std::endl;
		return false;
	}
	char* buffer = new char[length];
	memset(buffer, 0, length);
	DWORD read_size = 0;
	if (!ReadFile(h, buffer, (DWORD)length, &read_size, NULL))
	{
		CloseHandle(h);
		delete[]buffer;
		wcout << L"Can't read the file " << file.c_str() << L", error is " << GetLastError() << std::endl;
		return false;
	}
	CloseHandle(h);
	cout << endl;
	cout << "Address\t\t";
	for (int i = 0; i < 16; i++)
	{
		cout << toHex(i) << "   ";
	} 
	cout << endl << endl << LongLongtoHex(offset) << "\t";
	LONGLONG line = 1;
	for (LONGLONG i = 0; i < read_size; i++)
	{
		unsigned char uc = static_cast<unsigned char>(buffer[i]);
		std::cout << toHex(uc) << "   ";
		if ((i+1) % 16 == 0 && i <read_size -1)
		{
			wcout << endl;
			cout << LongLongtoHex(offset + line * 16) << "\t";			
			line++;
		}
	}
	delete[]buffer;
	cout << endl;
	return true;
}
FILE_SYSTEM_MODE fsmode = FILE_SYSTEM_MODE::NORMAL_MODE;
LONGLONG bitmap_size = 0;
void MountFS(const std::wstring& data_path, const std::wstring& mount_point, LONGLONG fs_size)
{
	if (_BackupFS)
	{
		_BackupFS.reset();
	}
	if (!_BackupFS)
	{
		_BackupFS = std::make_shared<BackupVFS>();
	}
	_BackupFS->SetMode(fsmode);
	if (bitmap_size != 0)
	{
		_BackupFS->SetBitmapGroupSize(bitmap_size);
		bitmap_size = 0;
	}
	if (!_BackupFS->Mount(data_path, mount_point, fs_size))
	{
		return;
	}
	_BackupFS->Wait();
}
BACKUPFS_CMD(getfssize, get the fs size)
{
	if (_BackupFS)
	{
		LONGLONG size = _BackupFS->GetFSSize();
		LONGLONG free_size = _BackupFS->GetFreeSize();
		Logger->Info("FS size ", size, ",Free size ", free_size);
	}
	return true;
}
BACKUPFS_CMD(setreadonly, <0 / 1> set readonly FS)
{
	int readonly = 0;
	if (args.size() > 0)
	{
		readonly = atoi(args[0].c_str());
	}
	if (_BackupFS)
	{
		_BackupFS->SetReadOnly(readonly);
	}
	BackupFS::Logger->Info("Set readonly done.");
	return true;
}
BACKUPFS_CMD(setsize, <size> set FS size)
{
	LONGLONG size = 0;
	if (args.size() == 0)
	{
		Logger->Error("Please input the file system size");
		return false;
	}
	if (_BackupFS)
	{
		size = ParseSizeWithUnit(args[0]);
		if (size == 0)
		{
			size = atoll(args[0].c_str());
		}
		_BackupFS->SetFsSize(size);
	}
	Logger->Info("Set file system size done.");
	return true;
}
BACKUPFS_CMD(mount, <data path> <mount point>[filesystem size] create or mount a BackupFS file system)
{
	if (args.size() < 2)
	{
		Logger->Error("Please input the data path,mount point");
		return false;
	}
	fsmode = FILE_SYSTEM_MODE::NORMAL_MODE;

	LONGLONG fs_size = 0;
	if (args.size() > 2)
	{
		fs_size = BackupFS::ParseSizeWithUnit(args[2]);
		if (fs_size == 0)
		{
			fs_size = atoll(args[2].c_str());
		}
	}
	std::thread t(&MountFS, S2W(args[0]), S2W(args[1]), fs_size);
	t.detach();
	for (int i = 0; i < 5; i++)
	{
		if (_BackupFS && _BackupFS->IsMounted())
		{
			Logger->Info("mount file system successfully");
			return true;
		}
		else
		{
			std::this_thread::sleep_for(std::chrono::seconds(1));
		}
	}
	return true;
}
BACKUPFS_CMD(remount, remount the Current BackupFS file system)
{
	if (args.size() < 2)
	{
		Logger->Error("Please input the data path,mount point");
		return false;
	}
	fsmode = FILE_SYSTEM_MODE::NORMAL_MODE;

	LONGLONG fs_size = 0;
	if (args.size() > 2)
	{
		fs_size = BackupFS::ParseSizeWithUnit(args[2]);
		if (fs_size == 0)
		{
			fs_size = atoll(args[2].c_str());
		}
	}
	if (!_BackupFS)
	{
		Logger->Error("The file system doesn't mount");
		return false;
	}
	if (!_BackupFS->Mount(S2W(args[0]), S2W(args[1]), fs_size))
	{
		return true;
	}
	Logger->Error("remoount failed");
	return false;
}

BACKUPFS_CMD(umount, Unmount the Current BackupFS file system)
{
	if (_BackupFS)
	{
		_BackupFS->Umount();
		_BackupFS.reset();
	}
	BackupFS::Logger->Info("Unmount the BackupFS file system");
	return true;
}
bool DiffFile(const std::wstring& file1, const std::wstring& file2)
{
	if (!fs::exists(file1) || !fs::exists(file2))
	{
		wcout << L"The file(s) not found" << endl;
		return false;
	}
	uintmax_t size1 = fs::file_size(file1);
	uintmax_t size2 = fs::file_size(file2);
	if (size1 != size2)
	{
		wcout << L"File size not match,the file " << file1.c_str() << " size is " << size1 << endl;
		wcout << L"The file " << file2.c_str() << " size is " << size2 << endl;
		return false;
	}
	HANDLE h1 = CreateFile(file1.c_str(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
	if (h1 == INVALID_HANDLE_VALUE)
	{
		wcout << L"Can't open the file " << file1.c_str() << L", error is " << GetLastError() << std::endl;
		return 1;
	}
	HANDLE h2 = CreateFile(file2.c_str(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
	if (h2 == INVALID_HANDLE_VALUE)
	{
		CloseHandle(h1);
		wcout << L"Can't open the file " << file2.c_str() << L", error is " << GetLastError() << std::endl;
		return false;
	}
	const int block_size = 65536;
	char* buffer1 = new char[block_size];
	char* buffer2 = new char[block_size];
	LONGLONG offset = 0;
	bool found_diff = false;
	while (true)
	{
		DWORD read_size1 = 0;
		DWORD read_size2 = 0;
		if (!ReadFile(h1, buffer1, block_size, &read_size1, NULL) or !ReadFile(h2, buffer2, block_size, &read_size2, NULL))
		{
			wcout << L"Read file failed" << endl;
			break;
		}
		if (read_size1 == 0)
		{
			break;
		}
		if (memcmp(buffer1, buffer2, read_size1) != 0)
		{
			int diff_num = 0;
			for (DWORD i = 0; i < read_size1; i++)
			{
				if (memcmp(buffer1+i, buffer2+i, 1) != 0)
				{			
					if (diff_num == 0)
					{
						offset += i;
					}
					diff_num++;
				}
			}
			Logger->Error("Found diff data,the offset is ", offset);
			Logger->Error("Diff bytes " ,diff_num );
			found_diff = true;
			break;
		}
		offset += read_size1;
	}
	if (!found_diff)
	{
		Logger->Info("The two files are identical");
	}
	Logger->Info("Diff completed");
	delete[]buffer1;
	delete[]buffer2;
	CloseHandle(h1);
	CloseHandle(h2);
	return !found_diff;
}
ULONGLONG FileSize(const std::wstring& file)
{
	if (!fs::exists(file))
	{
		wcout << "The file " << file << " doesn't exist" << endl;
		return 0;
	}
	ULONGLONG file_size = fs::file_size(file);
	cout << endl;
	cout << "File size:" << file_size << endl;
	return file_size;
}
BACKUPFS_CMD(hexdump, <file name> <offset> <length> Dump the file content with Hex)
{
	if (args.size() < 3)
	{
		cout << "Please input the file name,offset and length" << endl;
		return false;
	}
	return HexDump(BackupFS::S2W(args[0]).c_str(), _wtoll(BackupFS::S2W(args[1]).c_str()), _wtoll(BackupFS::S2W(args[2]).c_str()));
}
bool CopySecurityAttributes(const std::wstring& sourcePath, const std::wstring& destinationPath) 
{
	PSECURITY_DESCRIPTOR pSD = NULL;
	DWORD dwSizeNeeded = 0;

	if (!GetFileSecurity(sourcePath.c_str(), DACL_SECURITY_INFORMATION | OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION, pSD, 0, &dwSizeNeeded)) 
	{
		if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) 
		{
			Logger->Error("GetFileSecurity failed with error: " , GetLastError());
			return false;
		}
	}

	pSD = (PSECURITY_DESCRIPTOR)LocalAlloc(LPTR, dwSizeNeeded);
	if (!pSD)
	{
		return false;
	}
	if (!GetFileSecurity(sourcePath.c_str(), DACL_SECURITY_INFORMATION | OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION, pSD, dwSizeNeeded, &dwSizeNeeded)) 
	{
		Logger->Error("GetFileSecurity failed with error: " , GetLastError());
		LocalFree(pSD);
		return false;
	}

	if (!SetFileSecurity(destinationPath.c_str(), DACL_SECURITY_INFORMATION | OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION, pSD)) 
	{
		Logger->Error("SetFileSecurity failed with error: ", GetLastError());
		LocalFree(pSD);
		return false;
	}
	LocalFree(pSD);
	Logger->Info("copy file security successfully");
	return true;
}
BACKUPFS_CMD(createnew, <file> new file)
{
	if (args.size() < 1)
	{
		Logger->Error("Please input the file name");
		return false;
	}
	HANDLE hFile;
	hFile = CreateFile(
		S2W(args[0]).c_str(),
		GENERIC_READ | GENERIC_WRITE, 
		0,
		NULL,
		CREATE_NEW,
		FILE_ATTRIBUTE_NORMAL, 
		NULL
	);

	if (hFile == INVALID_HANDLE_VALUE) 
	{
		
		Logger->Error("CreateFile failed with error: ", GetLastError()); 
		return false;
	}

	Logger->Info("File created successfully.");
	CloseHandle(hFile);
	return true;
}
BACKUPFS_CMD(copysecurity, <source><target> copy the file security)
{
	if (args.size() < 2)
	{
		Logger->Error("Please input the source and target files");
		return false;
	}
	return CopySecurityAttributes(S2W(args[0]), S2W(args[1]));
}
BACKUPFS_CMD(truncatetozero, <file> truncate a file to zero)
{
	if (args.size() < 1)
	{
		Logger->Error("Please input the file");
		return false;
	}
	HANDLE hFile = CreateFile(S2W(args[0]).c_str(),
		GENERIC_WRITE,
		FILE_SHARE_WRITE | FILE_SHARE_READ,
		NULL,
		CREATE_ALWAYS,
		FILE_ATTRIBUTE_NORMAL,
		NULL);

	if (hFile == INVALID_HANDLE_VALUE) 
	{
		Logger->Error("CreateFile failed with error ", GetLastError());
		return false;
	}
	CloseHandle(hFile);
	return true;
}
BACKUPFS_CMD(filesize, <file name> show the file size)
{
	if (args.size() < 1)
	{
		cout << "Please input the file" << endl;
		return false;
	}
	ULONGLONG file_size = FileSize(BackupFS::S2W(args[0]));
	if (args.size() > 1)
	{
		ULONGLONG given_size = atoll(args[1].c_str());
		if (given_size != file_size)
		{
			Logger->Error("file size doesnot match,", args[0],",",file_size,",", given_size);
			return false;
		}
		Logger->Error("file size match,", args[0], ",", file_size, ",", given_size);
	}
	return true;
	
}
BACKUPFS_CMD(diff, <file1> <file2> compare the two files content)
{
	if (args.size() < 2)
	{
		std::cout << "Please input two files" << endl;
		return false;
	}
	std::wstring file1 = BackupFS::S2W(args[0]);
	std::wstring file2 = BackupFS::S2W(args[1]);
	bool res = DiffFile(file1, file2);
	if (res)
	{
		Logger->Info("The two files are identical");
	}
	Logger->Info("Diff completed");
	return res;
}
bool compareFiles(const fs::path& file1, const fs::path& file2) 
{
	std::ifstream f1(file1, std::ios::binary | std::ios::ate);
	std::ifstream f2(file2, std::ios::binary | std::ios::ate);

	if (f1.fail() || f2.fail()) 
	{
		Logger->Error(file1.string(), " failed or ", file2.string(), " failed");
		return false; 
	}

	if (f1.tellg() != f2.tellg()) 
	{
		Logger->Error(file1.string(), " and ", file2.string(), " length are different");
		return false; 
	}

	f1.seekg(0, std::ios::beg);
	f2.seekg(0, std::ios::beg);
	const std::size_t bufferSize = 65536; 
	std::vector<char> buffer1(bufferSize);
	std::vector<char> buffer2(bufferSize);
	while (f1.read(buffer1.data(), bufferSize) && f2.read(buffer2.data(), bufferSize)) 
	{
		if (buffer1 != buffer2) 
		{
			Logger->Error("Two Files are different");
			return false;
		}
	}

	return true;
}

bool compareDirectories(const fs::path& dir1, const fs::path& dir2)
{
	if (!fs::exists(dir1) || !fs::is_directory(dir1))
	{
		Logger->Error("folder ", dir1.string(), " doesn't exist");
		return false;
	}

	if (!fs::exists(dir2) || !fs::is_directory(dir2))
	{
		Logger->Error("folder ",dir2.string(), " doesn't exist");
		return false;
	}

	for (const auto& entry1 : fs::recursive_directory_iterator(dir1))
	{
		const auto& path1 = entry1.path();
		const auto relativePath = fs::relative(path1, dir1);
		const auto path2 = dir2 / relativePath;

		if (fs::is_regular_file(path1))
		{
			if (!fs::exists(path2))
			{
				Logger->Error(path2.string(), " doesn't exist");
				return false;
			}
			else if (!DiffFile(path1, path2))
			{
				return false;
			}
		}
		else if (fs::is_directory(path1))
		{
			if (!fs::exists(path2) || !fs::is_directory(path2))
			{
				Logger->Error(path2.string(), " doesn't exist");
				return false;
			}
		}
	}
	return true;
}

BACKUPFS_CMD(difffolder, <folder 1> <folder 2> compare the two folders)
{
	if (args.size() < 2)
	{
		Logger->Error("Please input the two folders");
		return false;
	}
	if (!compareDirectories(args[0], args[1]))
	{
		Logger->Error("Two folders are different");
		return false;
	}
	Logger->Info("Two folder are identical");
	return true;
}
BACKUPFS_CMD(fscp, <source file> <target file> copy the source file to target file)
{
	if (args.size() < 2)
	{
		cout << "Please input the source and target files" << endl;
		return false;
	}

	if (!_BackupFS)
	{
		return false;
	}
	std::wstring source_file = BackupFS::S2W(args[0]);
	std::wstring target_file = BackupFS::S2W(args[1]);
	if (!fs::exists(source_file))
	{
		cout << "The file " << args[0] << " not found" << endl;
		return false;
	}
	LONGLONG file_size = fs::file_size(source_file);
	if (!_BackupFS->CreateFile(target_file, file_size, 32))
	{
		Logger->Error("create file failed");
		return false;
	}
	const int BUFFER_SIZE = 1024 * 1024;
	char* buffer = new char[BUFFER_SIZE];
	bool failed = false;
	LONGLONG offset = 0;
	while (true)
	{
		DWORD read_size = 0;
		DWORD written_size = 0;
		if (!_BackupFS->ReadFile(source_file,buffer, BUFFER_SIZE, &read_size, offset))
		{
			failed = true;
			wcout << L"Read File " << source_file << L" Failed,error is " << GetLastError() << endl;
			break;
		}

		if (read_size <= 0)
		{
			break;
		}
		if (!_BackupFS->WriteFile(target_file, buffer, read_size, &written_size, offset))
		{
			failed = true;
			wcout << L"Write File " << target_file << L" Failed,error is " << GetLastError() << endl;
			break;
		}
		offset += read_size;

	}
	delete[]buffer;
	_BackupFS->CloseFile(source_file);
	_BackupFS->CloseFile(target_file);
	if (failed)
	{
		Logger->Error("fscp file failed");
		return false;
	}
	return true;

}
BACKUPFS_CMD(cp, <source file> <target file> copy the source file to target file)
{

	if (args.size() < 2)
	{
		cout << "Please input the source and target files" << endl;
		return false;
	}
	std::wstring source_file = BackupFS::S2W(args[0]);
	std::wstring target_file = BackupFS::S2W(args[1]);
	if (!fs::exists(source_file))
	{
		cout << "The file "<< args[0] << " not found" << endl;
		return false;
	}
	if (fs::exists(target_file))
	{
		fs::remove(target_file);
	}
	HANDLE source = CreateFile(source_file.c_str(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
	if (source == INVALID_HANDLE_VALUE)
	{
		wcout << L"Can't open the file " << source_file.c_str() << L", error is " << GetLastError() << std::endl;
		return false;
	}
	HANDLE target = CreateFile(target_file.c_str(), GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
	if (target == INVALID_HANDLE_VALUE)
	{
		CloseHandle(source);
		wcout << L"Can't open the file " << target_file.c_str() << L", error is " << GetLastError() << std::endl;
		return false;
	}
	LONGLONG file_size = fs::file_size(source_file);
	
	SetFileValidData(target, file_size);
	const int BUFFER_SIZE = 1024 * 1024;
	char* buffer = new char[BUFFER_SIZE];
	bool failed = false;
	LONGLONG completed_size = 0;
	Logger->Info("Begin to copy file");
	std::chrono::system_clock::time_point t1 = std::chrono::system_clock::now();
	while (true)
	{
		DWORD read_size = 0;
		DWORD written_size = 0;
		if (!ReadFile(source, buffer, BUFFER_SIZE, &read_size, NULL))
		{
			failed = true;
			wcout << L"Read File " << source_file << L" Failed,error is " << GetLastError() << endl;
			break;
		}

		if (read_size <= 0)
		{
			break;
		}
		if (!WriteFile(target, buffer, read_size, &written_size,NULL))
		{
			failed = true;
			wcout << L"Write File " << target_file << L" Failed,error is " << GetLastError() << endl;
			break;
		}
		completed_size += read_size;

	}
	delete[]buffer;

	CloseHandle(source);
	CloseHandle(target);
	std::chrono::system_clock::time_point t2 = std::chrono::system_clock::now();

	std::chrono::duration<double> elapsed_seconds = t2 - t1;
	if (!failed)
	{
		Logger->Info("Copy completed,Elapsed time: " , elapsed_seconds.count() , " seconds");
		Logger->Info("The average speed is: ", (double)file_size / elapsed_seconds.count() / 1024 / 1024, " MB/S");
	}

	return true;
}
void GenFilesWithFSAPI(const std::wstring& folder, LONGLONG file_size, LONGLONG file_number, LONGLONG start_number, bool* failed)
{
	if (!_BackupFS)
	{
		*failed = false;
		return;
	}
	Logger->Info("generate file with BackupFS file system API");
	const int BLOCK_SIZE = 1024 * 1024;
	char* buffer = new char[BLOCK_SIZE];
	memset(buffer, 'x', BLOCK_SIZE);
	std::wstring new_folder = folder;
	if (new_folder[new_folder.length() - 1] == '\\')
	{
		new_folder = new_folder.substr(0, new_folder.length() - 1);
	}
	for (LONGLONG i = start_number; i < file_number + start_number; i++)
	{
		
		std::wstring file_name = new_folder + L"\\" + std::to_wstring(i);
		if(!_BackupFS->CreateFile(file_name,file_size,32))
		{
			*failed = true;
			Logger->Error("Open file ", W2S(file_name), " failed,error is ", GetLastError());
			break;
		}

		DWORD written_size = 0;
		LONGLONG offset = 0;
		for (LONGLONG j = 0; j < file_size; j += (LONGLONG)written_size)
		{
			DWORD need_to_write_size = BLOCK_SIZE;
			if (file_size < BLOCK_SIZE)
			{
				need_to_write_size = (DWORD)file_size;
			}
			if (j + need_to_write_size > file_size)
			{
				need_to_write_size = DWORD(file_size - j);
			}
			if (!_BackupFS->WriteFile(file_name, buffer, need_to_write_size, &written_size,offset))
			{
				*failed = true;
				Logger->Error("WriteFile file ", W2S(file_name), " failed,error is ", GetLastError());
				break;
			}
			offset += written_size;
		}
		_BackupFS->CloseFile(file_name);
	}
	delete[]buffer;
	if (!*failed)
	{
		Logger->Info("Generate files completed from start ", start_number);
	}
}

void GenFiles(const std::wstring& folder, LONGLONG file_size, LONGLONG file_number,LONGLONG start_number,bool *failed)
{
	const int BLOCK_SIZE = 1024 * 1024;
	char* buffer = new char[BLOCK_SIZE];
	memset(buffer, 'x', BLOCK_SIZE);
	for (LONGLONG i = start_number; i < file_number + start_number; i++)
	{
		std::wstring file_name = folder + L"\\" + std::to_wstring(i);
		HANDLE file = CreateFile(file_name.c_str(), GENERIC_WRITE, 0, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
		if (file == INVALID_HANDLE_VALUE)
		{
			*failed = true;
			Logger->Error("Open file ", W2S(file_name), " failed,error is ", GetLastError());
			break;
		}
		if (file_size > BLOCK_SIZE)
		{
			SetFileValidData(file, file_size);
		}

		DWORD written_size = 0;
		for (LONGLONG j = 0; j < file_size; j += (LONGLONG)written_size)
		{
			DWORD need_to_write_size = BLOCK_SIZE;
			if (file_size < BLOCK_SIZE)
			{
				need_to_write_size = (DWORD)file_size;
			}
			if (j + need_to_write_size > file_size)
			{
				need_to_write_size = DWORD(file_size - j);
			}
			if (!WriteFile(file, buffer, need_to_write_size, &written_size, NULL))
			{
				*failed = true;
				Logger->Error("WriteFile file ", W2S(file_name), " failed,error is ", GetLastError());
				break;
			}
		}
		if (file != INVALID_HANDLE_VALUE)
		{
			CloseHandle(file);
		}
	}
	delete[]buffer;
	if (!*failed)
	{
		Logger->Info("Generate files completed from start ", start_number);
	}
}
BACKUPFS_CMD(genfiles, <folder path> <size> <file number> generate files and each file size is size)
{
	if (args.size() < 3)
	{
		Logger->Error("Please input the folder , file size and file number");
		return false;
	}
	int thread_num = 1;
	bool use_windows_api = true;
	LONGLONG file_size = BackupFS::ParseSizeWithUnit(args[1]);
	if (file_size == 0)
	{
		file_size = atoll(args[1].c_str());
	}
	LONGLONG file_number = atoll(args[2].c_str());
	if (args.size() > 3)
	{
		thread_num = atoi(args[3].c_str());
	}
	if (args.size() > 4)
	{
		use_windows_api = false;
	}
	Logger->Info("Folder ", args[0], " file size ", file_size, " total file number ", file_number, " thread number ", thread_num);
	std::vector<std::future<void>> threads;
	LONGLONG number = file_number / thread_num;
	std::chrono::system_clock::time_point t1 = std::chrono::system_clock::now();
	bool failed = false;
	for(int i=0;i<thread_num;i++)
	{
		threads.emplace_back(std::async(std::launch::async, use_windows_api?GenFiles:GenFilesWithFSAPI, S2W(args[0]), file_size, number, i * number,&failed));
		
	}
	for (auto &t : threads)
	{
		t.get();
	}

	std::chrono::system_clock::time_point t2 = std::chrono::system_clock::now();
	std::chrono::duration<double> elapsed_seconds = t2 - t1;

	Logger->Info("Generate files completed");
	Logger->Info("Totoal time:", elapsed_seconds.count(), " seconds");
	if (file_number < 10)
	{
		Logger->Info("The average speed:", (file_size * file_number) / 1024 / 1024 / elapsed_seconds.count(), " MB/S");
	}
	else
	{
		if (!failed)
		{
			Logger->Info("Generate files ", (file_number) / elapsed_seconds.count(), " each second");
		}
	}
	
	return true;
}
BACKUPFS_CMD(fsreaddir, <dir name> read all the files of a directoy)
{
	if (!_BackupFS)
	{
		Logger->Error("Please mount the file system firstly");
		return false;
	}
	if (args.size() < 1)
	{
		Logger->Error("Please input the folder name");
		return false;
	}
	fs::path dir_path = args[0];
	std::vector<fs::path> file_paths;

	auto traverse_start = std::chrono::system_clock::now();
	try
	{
		for (const auto& entry : fs::recursive_directory_iterator(dir_path))
		{
			if (entry.is_regular_file())
			{
				file_paths.push_back(entry.path());
			}
		}
	}
	catch (...)
	{
		Logger->Error("Failed");
		return 1;
	}
	auto traverse_end = std::chrono::system_clock::now();
	std::chrono::duration<double> elapsed_seconds = traverse_end - traverse_start;
	Logger->Info("Traverse totoal time:", elapsed_seconds.count(), " seconds");
	auto read_start = std::chrono::system_clock::now();
	char buffer[65536] = { 0 };
	for (const auto& file_path : file_paths)
	{
		DWORD read_size = 0;
		if (!_BackupFS->ReadFile(file_path.wstring(), buffer, 65536, &read_size, 0))
		{
			Logger->Error("ReadFile failed, error is ", GetLastError());
			break;
		}
	}
	auto read_end = std::chrono::system_clock::now();
	elapsed_seconds = read_end - read_start;
	Logger->Info("Read totoal time:", elapsed_seconds.count(), " seconds");
	return true;

}
BACKUPFS_CMD(readdir, <dir name> read all the files of a directoy)
{
	if (args.size() < 1)
	{
		Logger->Error("Please input the folder name");
		return false;
	}
	fs::path dir_path = args[0];

	std::vector<fs::path> file_paths;

	auto traverse_start = std::chrono::system_clock::now();
	try 
	{
		for (const auto& entry : fs::recursive_directory_iterator(dir_path)) 
		{
			if (entry.is_regular_file()) 
			{
				file_paths.push_back(entry.path());
			}
		}
	}
	catch (...) 
	{
		Logger->Error("Failed");
		return 1;
	}
	auto traverse_end = std::chrono::system_clock::now();
	std::chrono::duration<double> elapsed_seconds = traverse_end - traverse_start;
	Logger->Info("Traverse totoal time:", elapsed_seconds.count(), " seconds");

	auto read_start = std::chrono::system_clock::now();
	for (const auto& file_path : file_paths) 
	{
		std::ifstream file(file_path, std::ios::binary);
		if (!file) 
		{
			Logger->Error("Failed to open file", file_path);
			continue;
		}
		file.seekg(0, std::ios::end);
		const auto size = file.tellg();
		file.seekg(0, std::ios::beg);

		std::vector<char> buffer(static_cast<size_t>(size));
		if (!file.read(buffer.data(), size)) 
		{
			Logger->Error("Failed to read file", file_path);
		}
	}
	auto read_end = std::chrono::system_clock::now();
	elapsed_seconds = read_end - read_start;
	Logger->Info("Read totoal time:", elapsed_seconds.count(), " seconds");
	return true;
}
BACKUPFS_CMD(read, <file name> read a file from begin to end)
{
	if (args.size() < 1)
	{
		Logger->Error("Please input the file name");
		return false;
	}
	if (!fs::exists(args[0]))
	{
		Logger->Error("file ", args[0], " doesn't exist");
		return false;
	}
	HANDLE file = CreateFile(S2W(args[0]).c_str(), GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
	if (file == INVALID_HANDLE_VALUE)
	{
		Logger->Error("Can't open the file ", args[0], " the error is ", GetLastError());
		return false;
	}

	std::chrono::system_clock::time_point t1 = std::chrono::system_clock::now();
	DWORD block_size = 1024 * 1024;
	if (args.size() > 1)
	{
		block_size = atoi(args[1].c_str());
	}
	Logger->Info("Block size ", block_size);
	char* buffer = new char[block_size];
	while (true)
	{
		DWORD read_size = 0;
		if (!ReadFile(file, buffer, block_size, &read_size, NULL))
		{
			Logger->Error("ReadFile failed, error is ", GetLastError());
			break;
		}
		if (read_size < block_size)
		{
			break;
		}
	}
	CloseHandle(file);
	LONGLONG file_size = fs::file_size(args[0]);
	std::chrono::system_clock::time_point t2 = std::chrono::system_clock::now();
	std::chrono::duration<double> elapsed_seconds = t2 - t1;
	Logger->Info("Read completed, the file size is ", file_size, " time is ", elapsed_seconds.count());
	Logger->Info("The average read speed is:", file_size / 1024 / 1024 / elapsed_seconds.count(), " MB/S");
	return true;
}
BACKUPFS_CMD(fsread, <file name> read a file from begin to end)
{
	if (args.size() < 1)
	{
		Logger->Error("Please input the file name");
		return false;
	}
	if (!_BackupFS)
	{
		return false;
	}
	if (!fs::exists(args[0]))
	{
		Logger->Error("file ", args[0], " doesn't exist");
		return false;
	}
	std::chrono::system_clock::time_point t1 = std::chrono::system_clock::now();
	const int block_size = 1024 * 1024;
	char* buffer = new char[block_size];
	LONGLONG offset = 0;
	std::wstring file_name = S2W(args[0]);
	while (true)
	{
		DWORD read_size = 0;
		if (!_BackupFS->ReadFile(file_name, buffer, block_size, &read_size, offset))
		{
			Logger->Error("ReadFile failed, error is ", GetLastError());
			break;
		}
		if (read_size < block_size)
		{
			break;
		}
		offset += read_size;
	}
	LONGLONG file_size = fs::file_size(args[0]);
	std::chrono::system_clock::time_point t2 = std::chrono::system_clock::now();
	std::chrono::duration<double> elapsed_seconds = t2 - t1;
	Logger->Info("Read completed, the file size is ", file_size, " time is ", elapsed_seconds.count());
	Logger->Info("The average read speed is:", file_size / 1024 / 1024 / elapsed_seconds.count(), " MB/S");
	return true;
}
HANDLE lock_file = INVALID_HANDLE_VALUE;
BACKUPFS_CMD(lockfile, <file name> <offset> <length> lock a file)
{
	if (args.size() < 3)
	{
		Logger->Error("Please input the file name,offset and length");
		return false;
	}
	std::wstring file_name = S2W(args[0]);
	if (lock_file != INVALID_HANDLE_VALUE)
	{
		CloseHandle(lock_file);
	}
	lock_file = CreateFile(file_name.c_str(),
		FILE_GENERIC_WRITE,
		FILE_SHARE_WRITE,
		NULL,
		OPEN_ALWAYS,
		FILE_ATTRIBUTE_NORMAL,
		NULL);

	if (lock_file == INVALID_HANDLE_VALUE)
	{
		Logger->Error("Could not open file");
		return false;
	}
	//LockFile()
	LONGLONG offset = atoll(args[1].c_str());
	LONGLONG length = atoll(args[2].c_str());
	LARGE_INTEGER loffset = { 0 };
	LARGE_INTEGER llength = { 0 };
	loffset.QuadPart = offset;
	llength.QuadPart = length;
	if (!LockFile(lock_file, loffset.LowPart, loffset.HighPart, llength.LowPart, llength.HighPart))
	{
		Logger->Error("Lock file failed:", GetLastError());
		CloseHandle(lock_file);
		return false;
	}
	Logger->Info("Lock file done.");
	return true;
}
BACKUPFS_CMD(unlockfile, <offset> <length>  unlock the file)
{
	if (args.size() < 2)
	{
		Logger->Error("Please input the offset and length");
		return false;
	}

	LONGLONG offset = atoll(args[0].c_str());
	LONGLONG length = atoll(args[1].c_str());
	LARGE_INTEGER loffset = { 0 };
	LARGE_INTEGER llength = { 0 };
	loffset.QuadPart = offset;
	llength.QuadPart = length;
	if (!UnlockFile(lock_file, loffset.LowPart, loffset.HighPart, llength.LowPart, llength.HighPart))
	{
		Logger->Error("UnLock file failed:", GetLastError());
		CloseHandle(lock_file);
		return false;
	}
	CloseHandle(lock_file);
	lock_file = INVALID_HANDLE_VALUE;
	Logger->Info("UnLock file done.");
	return true;
}
BACKUPFS_CMD(fscreatefile, <file name> <file size> <data> create a file with BackupFS file system API)
{
	if (args.size() < 3)
	{
		Logger->Error("Please input the file name,length and data");
		return false;
	}
	if (_BackupFS)
	{
		LONGLONG file_size = atoll(args[1].c_str());
		std::wstring file_name = S2W(args[0]);
		if (!_BackupFS->CreateFile(file_name, file_size, 32))
		{
			Logger->Error("fscreatefile failed");
			return false;
		}
		
		LONGLONG written_size = 0;
		LONGLONG offset = 0;
		while (written_size < file_size)
		{
			DWORD size = 0;
			DWORD need_to_write_size = (DWORD)args[2].size();
			if (file_size - written_size < need_to_write_size)
			{
				need_to_write_size = (DWORD)(file_size - written_size);
			}
			if (!_BackupFS->WriteFile(file_name, args[2].c_str(), need_to_write_size, &size, offset))
			{
				BackupFS::Logger->Error("Can't write the file:", GetLastError());
				break;
			}
			written_size += size;
			offset += size;
			if (written_size >= file_size)
			{
				break;
			}
		}
		BackupFS::Logger->Info("Create file successfully.");
	}
	return true;
}
BACKUPFS_CMD(createfile, <file name> <file size> <data> create a file with name and size)
{
	if (args.size() < 2)
	{
		BackupFS::Logger->Error("Please input the file name,size and data");
		return false;
	}
	std::wstring file_name = BackupFS::S2W(args[0]);
	try
	{
		if (fs::exists(file_name))
		{
			fs::remove(file_name);
		}
	}
	catch (...)
	{

	}
	HANDLE file = CreateFile(file_name.c_str(), GENERIC_WRITE, NULL, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
	if (file == INVALID_HANDLE_VALUE)
	{
		BackupFS::Logger->Error("Can't Create the file:",GetLastError());
		return false;
	}
	LONGLONG file_size = atoll(args[1].c_str());	
	LONGLONG written_size = 0;
	while (written_size < file_size)
	{
		DWORD size = 0;
		DWORD need_to_write_size = (DWORD)args[2].size();
		if (file_size - written_size < need_to_write_size)
		{
			need_to_write_size = (DWORD)(file_size - written_size);
		}
		if (!WriteFile(file, args[2].c_str(), need_to_write_size, &size, NULL))
		{
			BackupFS::Logger->Error("Can't write the file:",GetLastError());
			break;
		}
		written_size += size;
		if (written_size >= file_size)
		{
			break;
		}
	}
	BackupFS::Logger->Info("Create file successfully.");

	CloseHandle(file);

	return true;
}
BACKUPFS_CMD(updatefile, <file name> <offset> <length> <data> update a file content)
{
	if (args.size() < 4)
	{
		BackupFS::Logger->Error("Please input the file name,offset,length and data");
		return false;
	}
	std::wstring file_name = BackupFS::S2W(args[0]);
	HANDLE file = CreateFile(file_name.c_str(), GENERIC_WRITE, NULL, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
	if (file == INVALID_HANDLE_VALUE)
	{
		BackupFS::Logger->Error("Can't Create the file:", GetLastError());
		return false;
	}
	LONGLONG offset = atoll(args[1].c_str());
	LONGLONG length = atoll(args[2].c_str());
	LONGLONG written_size = 0;
	LARGE_INTEGER file_offset = { 0 };
	file_offset.QuadPart = offset;
	if (!SetFilePointerEx(file, file_offset, NULL, FILE_BEGIN))
	{
		BackupFS::Logger->Error("Can seek the file:", GetLastError());
		CloseHandle(file);
		return false;
	}
	bool res = true;
	const int buffer_size = 65536;
	char buffer[65536] = { 0 };
	LONGLONG str_size = args[3].size();
	size_t numLoops = (buffer_size + str_size - 1) / str_size;

	for (size_t i = 0; i < numLoops; ++i) 
	{
		size_t start = i * str_size;
		if (start + str_size > buffer_size)
		{
			memcpy(buffer + start, args[3].c_str(), buffer_size - start);
		}
		else 
		{
			memcpy(buffer + start, args[3].c_str(), str_size);
		}
	}

	while (written_size < length)
	{
		DWORD size = 0;
		DWORD need_to_write_size = buffer_size;
		if (length - written_size < need_to_write_size)
		{
			need_to_write_size = (DWORD)(length - written_size);
		}
		if (!WriteFile(file, buffer, need_to_write_size, &size, NULL))
		{
			BackupFS::Logger->Error("Can't write the file:", GetLastError());
			res = false;
			break;
		}
		written_size += size;
		if (written_size >= length)
		{
			BackupFS::Logger->Info("Update file successfully.");
			break;
		}
	}
	CloseHandle(file);

	return res;
}
BACKUPFS_CMD(setfileattr, <file name> <attributes> set file attribute)
{
	if (args.size() < 2)
	{
		Logger->Error("Please input the file name and attributes");
		return false;
	}
	if (SetFileAttributes((S2W(args[0])).c_str(), atoi(args[1].c_str())))
	{
		Logger->Info("set file attributes successfully");
		return true;
	}
	Logger->Error("set file attribute failed:", GetLastError());
	return false;
}
BACKUPFS_CMD(getfileattr, <file name> <attributes> get file attribute)
{
	if (args.size() < 1)
	{
		Logger->Error("Please input the file name");
		return false;
	}
	DWORD attr = GetFileAttributes((S2W(args[0])).c_str());
	if (args.size() > 1)
	{
		DWORD given_attr = atoi(args[1].c_str());
		if (given_attr == (attr & given_attr))
		{
			Logger->Info("get file attr successfully");
			return true;
		}
		else
		{
			Logger->Info("get file attr failed:",GetLastError());
			return false;
		}
	}
	return true;
}
BACKUPFS_CMD(setfiletime, <file name> <time> set file attribute)
{
	if (args.size() < 2)
	{
		Logger->Error("Please input the file name and time");
		return false;
	}
	HANDLE hFile = CreateFile(S2W(args[0]).c_str(), GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
	if (hFile == INVALID_HANDLE_VALUE)
	{
		Logger->Error("Can't open file:",GetLastError());
		return false;
	}
	FILETIME file_time;
	BackupFSUtils::TimetToFileTime(atoll(args[1].c_str()), &file_time);
	if (!SetFileTime(hFile, &file_time, &file_time, &file_time))
	{
		Logger->Error("SetFileTime failed:", GetLastError());
		CloseHandle(hFile);
		return false;
	}

	CloseHandle(hFile);
	return true;
}
BACKUPFS_CMD(getfiletime, <file name> <time> get file attribute)
{
	if (args.size() < 1)
	{
		Logger->Error("Please input the file name");
		return false;
	}
	HANDLE hFile = CreateFile(S2W(args[0]).c_str(), GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
	if (hFile == INVALID_HANDLE_VALUE)
	{
		Logger->Error("Can't open file:", GetLastError());
		return false;
	}
	FILETIME create_time,access_time,write_time;
	if (!GetFileTime(hFile, &create_time, &access_time, &write_time))
	{
		Logger->Error("GetFileTime failed:", GetLastError());
		CloseHandle(hFile);
		return false;
	}
	LONGLONG t = BackupFSUtils::FileTimeToTime(&create_time);
	if (args.size() > 1)
	{
		LONGLONG given_tm = atoll(args[1].c_str());
		if (t != given_tm)
		{
			Logger->Error("Get file tiem failed");

			CloseHandle(hFile);
			return false;
		}
	}
	CloseHandle(hFile);
	return true;
}
BACKUPFS_CMD(setfileinfo, <file name> <file time> <attr> set file information)
{
	if (args.size() < 3)
	{
		Logger->Error("Please input the file name,file tiem and attr");
		return false;
	}
	//set the file time and attr
	HANDLE hFile = CreateFile(S2W(args[0]).c_str(), GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
	if (hFile == INVALID_HANDLE_VALUE)
	{
		Logger->Error("Can't open file:",GetLastError());
		return false;
	}
	FILE_BASIC_INFO fileInfo;
	if (!GetFileInformationByHandleEx(hFile, FileBasicInfo, &fileInfo, sizeof(fileInfo))) 
	{
		CloseHandle(hFile);
		return false;
	}
	FILETIME file_time;
	BackupFSUtils::TimetToFileTime(atoll(args[1].c_str()), &file_time);
	fileInfo.LastWriteTime.HighPart = file_time.dwHighDateTime;
	fileInfo.LastWriteTime.LowPart = file_time.dwLowDateTime;
	fileInfo.FileAttributes = atoi(args[2].c_str());
	if (!SetFileInformationByHandle(hFile, FileBasicInfo, &fileInfo, sizeof(fileInfo)))
	{
		Logger->Error("Set file info failed:", GetLastError());

		CloseHandle(hFile);
		return false;
	}
	CloseHandle(hFile);
	return true;
}
BACKUPFS_CMD(getfileinfo, <file name> [time] [attr] get file information)
{
	if (args.size() < 1)
	{
		Logger->Error("Please input the file name");
		return false;
	}
	//set the file time and attr
	HANDLE hFile = CreateFile(S2W(args[0]).c_str(), GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
	if (hFile == INVALID_HANDLE_VALUE)
	{
		Logger->Error("Can't open file:", GetLastError());
		return false;
	}
	BY_HANDLE_FILE_INFORMATION fileInfo;
	if (!GetFileInformationByHandle(hFile, &fileInfo))
	{
		Logger->Error("Get file info failed:", GetLastError());
		return false;
	}
	CloseHandle(hFile);
	if (args.size() > 2)
	{
		//check the change time and attr
		LONGLONG change_time = atoll(args[1].c_str());
		DWORD attr = atoi(args[2].c_str());
		if (fileInfo.dwFileAttributes != attr)
		{
			Logger->Error("attr check failed");
			return false;
		}
		FILETIME file_time;
		BackupFSUtils::TimetToFileTime(change_time, &file_time);
		if (file_time.dwHighDateTime != fileInfo.ftLastWriteTime.dwHighDateTime || file_time.dwLowDateTime != fileInfo.ftLastWriteTime.dwLowDateTime)
		{
			Logger->Error("check time failed");
			return false;
		}
	}
	return true;
}
BACKUPFS_CMD(disksize, <drive name> get the disk size of drive)
{
	if (args.size() < 1)
	{
		Logger->Error("Please input the drive name");
		return false;
	}
	ULARGE_INTEGER free_bytes, total_bytes, total_free_bytes;
	if (!GetDiskFreeSpaceEx(S2W(args[0]).c_str(), &free_bytes, &total_bytes, &total_free_bytes))
	{
		Logger->Error("get disk size failed:", GetLastError());
		return false;
	}
	return true;
}
BACKUPFS_CMD(findfiles, <drive name> find the files)
{
	if (args.size() < 1)
	{
		Logger->Error("Please input the drive name");
		return false;
	}
	
	WIN32_FIND_DATA FindFileData;
	HANDLE hFind;
	
	hFind = FindFirstFile(S2W(args[0]).c_str(), &FindFileData);
	if (hFind == INVALID_HANDLE_VALUE) 
	{
		Logger->Error("find file failed:", GetLastError());
		return false;
	}
	bool ok = false;
	while (FindNextFile(hFind, &FindFileData)) 
	{
		if (FindFileData.dwFileAttributes != FILE_ATTRIBUTE_DIRECTORY) 
		{
			Logger->Info(W2S(FindFileData.cFileName));
			ok = true;
		}
	}
	FindClose(hFind);
	return ok;
}
BACKUPFS_CMD(volumeinfo, <drive name> get the volume information)
{
	if (args.size() < 1)
	{
		Logger->Error("Please input the drive name");
		return false;
	}
	wchar_t volumeName[MAX_PATH];
	wchar_t fileSystemName[MAX_PATH];
	DWORD serialNumber;
	DWORD maxComponentLength;
	DWORD fileSystemFlags;
	if (!GetVolumeInformation(S2W(args[0]).c_str(), volumeName, MAX_PATH, &serialNumber, &maxComponentLength, &fileSystemFlags, fileSystemName, MAX_PATH)) 
	{
		Logger->Error("get volume info failed:", GetLastError());
		return false;
	}
	std::wstring file_system_name = fileSystemName;
	if (file_system_name != L"BackupFS")
	{
		Logger->Error("fiel system name is invalid");
		return false;
	}

	return true;
}

BACKUPFS_CMD(flushfilebuffer, <file name> flush file buffer)
{
	if (args.size() < 1)
	{
		Logger->Error("Please input the file name");
		return false;
	}
	HANDLE hFile = CreateFile(S2W(args[0]).c_str(), GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
	if (hFile == INVALID_HANDLE_VALUE)
	{
		Logger->Error("Can't open file:", GetLastError());
		return false;
	}
	BOOL ok = FlushFileBuffers(hFile);
	CloseHandle(hFile);
	if (!ok)
	{
		Logger->Error("Flush file buffer failed:", GetLastError());
	}
	return ok;
}

BACKUPFS_CMD(setflushfrequency, <frequency> set the filesystem flush frequency(seconds))
{
	if (args.size() < 1)
	{
		BackupFS::Logger->Error("Please input the frequency");
		return false;
	}
	if (_BackupFS)
	{
		_BackupFS->SetFlushFrequency(atoi(args[0].c_str()));
		BackupFS::Logger->Info("Set the file system flush frequency to ", args[0]);
	}
	return true;
}
BACKUPFS_CMD(setcachenumber, <cachenumber> set the filesystem cache number)
{
	if (args.size() < 1)
	{
		BackupFS::Logger->Error("Please input the cache number");
		return false;
	}
	if (_BackupFS)
	{
		_BackupFS->SetCacheNumber(atoll(args[0].c_str()));
	}
	return true;
}
BACKUPFS_CMD(getmountpoint, get the fs mount point)
{
	if (_BackupFS)
	{
		std::wstring mpoint = _BackupFS->GetMountPoint();
		if (args.size() > 0)
		{
			//check the mount point whether == the given value
			std::wstring gpoint = S2W(args[0]);
			if (gpoint == mpoint)
			{
				return true;
			}
			Logger->Error("The mount point doesn't match.",args[0],",",W2S(mpoint));
			return false;
		}

		return false;
	}
	return false;
}
BACKUPFS_CMD(mkdir, <dir name> make a directory)
{
	if (args.size() < 1)
	{
		Logger->Error("Please input the directory name");
		return false;
	}
	try
	{
		if (fs::create_directories(args[0]))
		{
			Logger->Info("Create directory successfully");
			return true;
		}
	}
	catch (...)
	{

	}
	Logger->Error("Create directory failed:", GetLastError());
	return false;
}
BACKUPFS_CMD(rmdir, <dir name> remove a directory)
{
	if (args.size() < 1)
	{
		Logger->Error("Please input the directory name");
		return false;
	}
	try
	{
		if (!fs::exists(args[0]))
		{
			return true;
		}
		if (fs::remove_all(args[0]))
		{
			Logger->Info("Remove directory successfully");
			return true;
		}
	}
	catch (...)
	{

	}
	Logger->Error("Remove directory failed:", GetLastError());
	return false;
}
BACKUPFS_CMD(exist, <file path> check whether the path exists)
{
	if (args.size() < 1)
	{
		Logger->Error("Please input the path");
		return false;
	}
	if (fs::exists(args[0]))
	{
		Logger->Info("The path existed");
		return true;
	}
	Logger->Error("The path does not existed");
	return false;
}
BACKUPFS_CMD(notexist, <file path> check whether the path exists)
{
	if (args.size() < 1)
	{
		Logger->Error("Please input the path");
		return false;
	}
	if (fs::exists(args[0]))
	{
		Logger->Error("The path existed");
		return false;
	}
	Logger->Info("The path does not existed");
	return true;
}
BACKUPFS_CMD(remove, <file path> remove a file or folder)
{
	if (args.size() < 1)
	{
		Logger->Error("Please input the path");
		return false;
	}
	bool res = false;
	try
	{
		if (fs::exists(args[0]))
		{
			if (fs::is_directory(args[0]))
			{
				res = fs::remove_all(args[0]);
			}
			else
			{
				res = fs::remove(args[0]);
			}
			if (res)
			{
				Logger->Info("Removed the path ");
			}
			else
			{
				Logger->Error("Remove the path failed:", GetLastError());
			}
		}
	}
	catch (...)
	{

	}
	return res;
}
BACKUPFS_CMD(rename, <source path> <target path> rename file or folder)
{
	if (args.size() < 2)
	{
		Logger->Error("Please input the source and target path");
		return false;
	}
	try
	{
		fs::rename(args[0], args[1]);
		if (fs::exists(args[1]))
		{
			Logger->Info("Rename successfully");
			return true;
		}
	}
	catch (...)
	{

	}
	Logger->Error("Rename failed:", GetLastError());
	return false;
}
BACKUPFS_CMD(times, <call times> compare the times performance)
{
	LONGLONG times = 1000000;
	if (args.size() > 0)
	{
		times = atoll(args[0].c_str());
	}
	Logger->Info("Compare some time functions' performance");
	LONGLONG times_begin = GetTickCount64();
	LONGLONG cur = 0;
	for (LONGLONG i = 0; i < times; i++)
	{
		cur = time(NULL);
	}
	LONGLONG times_end = GetTickCount64();
	LONGLONG tick_begin = GetTickCount64();
	for (LONGLONG i = 0; i < times; i++)
	{
		cur = GetTickCount64();
	}
	LONGLONG tick_end = GetTickCount64();
	Logger->Info("time(NULL) costs ", times_end - times_begin, " ms");
	Logger->Info("GetTickCount64 costs ", tick_end - tick_begin, " ms");
	return true;

}
BACKUPFS_CMD(incremental, <data path> <mount point>[filesystem size] create or mount a BackupFS file system with incremental mode)
{
	if (args.size() < 2)
	{
		BackupFS::Logger->Error("Please input the data path,mount point");
		return false;
	}
	LONGLONG fs_size = 0;
	if (args.size() > 2)
	{
		fs_size = BackupFS::ParseSizeWithUnit(args[2]);
		if (fs_size == 0)
		{
			fs_size = atoll(args[2].c_str());
		}
	}
	fsmode = FILE_SYSTEM_MODE::INCREMENTAL_MODE;

	std::thread t(&MountFS, BackupFS::S2W(args[0]), BackupFS::S2W(args[1]), fs_size);
	t.detach();
	for (int i = 0; i < 5; i++)
	{
		if (_BackupFS && _BackupFS->IsMounted())
		{
			Logger->Info("mount file system successfully");
			return true;
		}
		else
		{
			std::this_thread::sleep_for(std::chrono::seconds(1));
		}
	}
	Logger->Info("mount file system failed");
	return false;
}
BACKUPFS_CMD(setbitmapgroupsize, <size> set the bitmap group size)
{
	if (args.size() < 1)
	{
		Logger->Error("Please input the size");
		return false;
	}
	bitmap_size = BackupFS::ParseSizeWithUnit(args[0]);
	if (bitmap_size == 0)
	{
		bitmap_size = atoll(args[0].c_str());
	}
	return true;
}
BACKUPFS_CMD(drill, <data path> <mount point>[filesystem size] create or mount a BackupFS file system with drill mode)
{
	if (args.size() < 2)
	{
		BackupFS::Logger->Error("Please input the data path,mount point");
		return false;
	}
	LONGLONG fs_size = 0;
	if (args.size() > 2)
	{
		fs_size = BackupFS::ParseSizeWithUnit(args[2]);
		if (fs_size == 0)
		{
			fs_size = atoll(args[2].c_str());
		}
	}
	fsmode = FILE_SYSTEM_MODE::DRILL_MODE;

	std::thread t(&MountFS, BackupFS::S2W(args[0]), BackupFS::S2W(args[1]), fs_size);
	t.detach();
	for (int i = 0; i < 5; i++)
	{
		if (_BackupFS && _BackupFS->IsMounted())
		{
			Logger->Info("mount file system successfully");
			return true;
		}
		else
		{
			std::this_thread::sleep_for(std::chrono::seconds(1));
		}
	}
	Logger->Info("mount file system failed");
	return false;
}
///the following cases for unit test
BACKUPFS_CMD(BackupFSUnit, BackupFS unit test)
{
	BackupVFS fs;
	fs.FsFileNodes = std::make_unique<BackupFSFileNodes>();
	fs.SetMountPoint(L"z:\\");
	std::this_thread::sleep_for(std::chrono::seconds(2));
	char buffer[1024] = { 0 };
	DWORD read_length = 0;
	std::wstring file_name = L"z:\\non-existed-file";
	fs.WriteFile(file_name, buffer, 1024, &read_length, 0);
	fs.ReadFile(file_name, buffer, 0, &read_length, 0);
	fs.CloseFile(file_name);
	return true;
}
BACKUPFS_CMD(BackupFSLRUUnit, BackupFSLRU unit test)
{

	BackupFSLRU<std::wstring, int> cache;
	cache.Update(L"test001", 0);
	cache.Update(L"test002", 1);
	cache.Update(L"test003", 2);
	cache.Update(L"test004", 3);
	Logger->Info("Cache size is ", cache.Size());
	cache.Remove(L"test002");
	Logger->Info("Cache size is after removed", cache.Size());
	bool removed = false;
	std::wstring last = cache.RemoveLast(removed);
	Logger->Info("Cache size is after removed last", cache.Size());
	Logger->Info("Last item is ", W2S(last));
	return true;
}
BACKUPFS_CMD(BackupFSDBUnit, BackupFSDB unit test)
{
	Logger->Info("BackupFSDB unit test starting");
	BackupFSDB db;
	//successfully cases
	std::wstring db_file = L"testdb.db";
	if (fs::exists(db_file))
	{
		fs::remove(db_file);
	}
	bool has_db = false;
	if (!db.Init(L"testdb.db", has_db))
	{
		Logger->Error("failed to init db");
		return false;
	}
	if (has_db)
	{
		Logger->Error("the db file existed,test failed.");
		return false;
	}
	if (!db.TableExist("files"))
	{
		Logger->Error("table files doesn't exist,failed");
		return false;
	}
	if (db.TableExist("notable"))
	{
		Logger->Error("table notable exist,failed");
		return false;
	}
	if (!db.GetDb())
	{
		Logger->Error("getdb failed");
		return false;
	}
	if (db.Execute("select * from notable;"))
	{
		Logger->Error("execute failed");
		return false;
	}
	if (!db.Execute("select * from files"))
	{
		Logger->Error("execute failed");
		return false;
	}
	db.Close();
	if (fs::exists(db_file))
	{
		fs::remove(db_file);
	}
	//failed cases
	db_file = L"xxx\\xxx.db";
	if (db.Init(db_file,has_db))
	{
		Logger->Error("Init failed");
		return false;
	}
	Logger->Info("BackupFSDB unit test end");
	return true;
}
BACKUPFS_CMD(truncate, <file name> <length> truncate a file)
{
	if (args.size() < 2)
	{
		Logger->Error("please input the file name and length");
		return false;
	}
	try
	{
		if (!fs::exists(args[0]))
		{
			Logger->Error("File ", args[0], " doesn't exist");
			return false;
		}
		LONGLONG length = atoll(args[1].c_str());
		std::error_code err;
		fs::resize_file(args[0], length,err);
		if (err)
		{
			Logger->Error("rename file failed:",GetLastError());
			return false;
		}
	}
	catch (...)
	{
		return false;
	}
	return true;
}
BACKUPFS_CMD(bitmaptest, <offset> <length> bitmap test)
{
	if (args.size() < 2)
	{
		Logger->Error("please input the offset and length");
		return false;
	}
	LONGLONG offset = atoll(args[0].c_str());
	LONGLONG length = atoll(args[1].c_str());
	std::shared_ptr<BackupFSBitmap> bitmap = std::make_shared<BackupFSBitmap>(L"",true,0);
	bitmap->Set(offset, length);
	std::shared_ptr<BackupFSBit>bits = std::make_shared<BackupFSBit>(0);
	bits->Set(offset, length);
	//get all the rest offset and length
	if (args.size() > 3)
	{
		for (LONGLONG i = 2; i < (LONGLONG)args.size(); i += 2)
		{
			LONGLONG offset = atoll(args[i].c_str());
			LONGLONG length = atoll(args[i+1].c_str());
			bitmap->Set(offset, length);
			bits->Set(offset, length);
		}
	}
	//compare the bitmap
	for (LONGLONG i = 0; i < BACKUPFS_BITMAP_GROUP_SIZE; i += BACKUPFS_PAGE_SIZE)
	{
		if (bitmap->IsSet(i) != bits->IsSet(i))
		{
			Logger->Error("bitmap doesnot match,the offset is ", i);
			return false;
		}
	}
	Logger->Info("bitmap matched");
	return true;
}
bool ReadAFile(const std::wstring& file_name, int buffer_size)
{
	HANDLE file = CreateFile(file_name.c_str(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
	if (file == INVALID_HANDLE_VALUE)
	{
		Logger->Error("Open file failed:", GetLastError());
		return false;
	}
	char* buffer = new char[buffer_size];
	bool res = true;
	while (true)
	{
		DWORD read_size = 0;
		if (!ReadFile(file, buffer, buffer_size, &read_size, NULL))
		{
			res = false;
			Logger->Error("Read file failed:", GetLastError());
			break;
		}
	}
	delete[]buffer;
	return res;
}
BACKUPFS_CMD(readafile, <file name> read a file)
{
	if (args.size() < 1)
	{
		Logger->Error("please input a file name");
		return false;
	}
	int buffer_size = 65536;
	bool res = ReadAFile(S2W(args[0]), buffer_size);
	if (!res)
	{
		Logger->Error("read file failed");
	}
	return res;
}
int wmain(int argc,wchar_t *argv[])
{
	if (fs::exists(L"fslog.txt"))
	{
		fs::remove(L"fslog.txt");
	}
	//Logger->SetLogTimeStamp(false);
	Logger->SetToConsole(true);
	Logger->SetLogFile(L"fslog.txt");
	Logger->Info("Log file name is fslog.txt");
	Logger->SetLogLevel(BackupFS::LevelInfo | BackupFS::LevelError | BackupFS::LevelWarning/* | BackupFS::LevelDebug*/);
	BackupFSShell cmd;
	bool ok = cmd.RunCmdLines("FileUtils", argc, argv);
	BackupFSLogger::ReleaseInstance();
	return ok ? 0 : 1;
}
